// GGX micro facet model
vec3 ImportanceSampleGGX( vec2 Xi, float Roughness, vec3 N )
{
  Xi.x = vMod(Xi.x, 1.0);
  Xi.y = vMod(Xi.y, 1.0);
	float a = Roughness * Roughness;
	float Phi = 2 * PI * Xi.x;
	float CosTheta = vSqrt( (1 - Xi.y) / ( 1 + (a*a - 1) * Xi.y ) );
	float SinTheta = vSqrt( 1 - CosTheta * CosTheta );
	vec3 H;
	H.x = SinTheta * cos( Phi );
	H.y = SinTheta * sin( Phi );
	H.z = CosTheta;
	vec3 UpVector = abs(N.z) < 0.999 ? vec3(0,0,1) : vec3(1,0,0);
	vec3 TangentX = vNormalize( vCross( UpVector, N ) );
	vec3 TangentY = vCross( N, TangentX );
	// Tangent to world space
	return TangentX * H.x + TangentY * H.y + N * H.z;
}

// Plane tools
struct Ray
{
	vec3 Start;
	vec3 Dir;
};

void ClipByPlane(Ray ray, vec4 plane, inout float t)
{
	float clipVal = vDot(plane, vec4(ray.Start, 1.0));
	float k = -clipVal / vDot(plane.xyz, ray.Dir);
	if (k < 0.0) return; // going out ?
	t = vMin(t, k);
}

// get linear z from "hyperbolic zbuffer"
float getLinearDepth(vec2 uv)
{
  float z = GetZ(uv);
  return ConvertZBufferToZView(z);
}

vec3 UVToViewVect(vec2 uv)
{
#ifdef PROJECTION_CONIC
	vec4 clipSpaceVect;
	clipSpaceVect.xy = 2.0 * uv - 1.0;
	clipSpaceVect.zw = vec2(0.0, 1.0);
	vec3 viewVect = vNormalize((cb_PostEffectAuto.u_ProjectionInverseMatrix * clipSpaceVect).xyz);
  return viewVect;
#else
  return vec3(0.0,0.0,-1.0);
#endif
}

vec3 UVToScreenEyeViewPos(vec2 uv)
{
#ifdef PROJECTION_CONIC
  return vec3(0.0);
#else
  vec4 clipSpaceVect;
	clipSpaceVect.xy = 2.0 * uv - 1.0;
	clipSpaceVect.zw = vec2(0.0, 1.0);
	return vec3((cb_PostEffectAuto.u_ProjectionInverseMatrix * clipSpaceVect).xy , 0.0);
#endif
}

vec3 UVAndDepthToViewPos(vec2 uv, float depth)
{
  vec3 viewVect = UVToViewVect(uv);
#ifdef PROJECTION_CONIC
  viewVect /= -viewVect.z;
#endif
  return depth*viewVect + UVToScreenEyeViewPos(uv);
}

vec3 GetViewRayPlaneInter(vec2 uv, vec3 vect, vec4 plane)
{
  Ray viewRay;
  viewRay.Start = UVToScreenEyeViewPos(uv);
  viewRay.Dir = vect;
  float inter = uintBitsToFloat(0x7f7fffff);
  ClipByPlane(viewRay, plane, inter);
  return viewRay.Start + inter * viewRay.Dir;
}

vec2 ViewPosToUV(vec3 viewPos)
{  
  vec4 hCoord = cb_PostEffectAuto.u_ProjectionMatrix * vec4(viewPos, 1.);
	vec2 uv;
	uv.x = 0.5 * hCoord.x / hCoord.w + 0.5;
	uv.y = 0.5 * hCoord.y / hCoord.w + 0.5;	
	return uv;
}

vec3 UVToViewPos(vec2 uv)
{
  float geomDepth = getLinearDepth(uv);
  return UVAndDepthToViewPos(uv, geomDepth);
}

#ifdef SSR_ZHierarchyRayMarshing
float getLinearDepthMip(vec2 uv, int mipLvl)
{
  float z = vTextureLod(ZMapWithMips, uv, float(mipLvl)).x;
  return ConvertZBufferToZView(z);
}

vec3 UVToViewPosMip(vec2 uv, int mipLvl)
{
  float geomDepth = getLinearDepthMip(uv, mipLvl);
  return UVAndDepthToViewPos(uv, geomDepth);
}
#endif

#ifdef SSR_LowResRayMarshing
float getLinearDepthLowRes(vec2 uv)
{
  float z = vTexture(LowResZMap, uv).x;
  return ConvertZBufferToZView(z);
}

vec3 UVToViewPosLowRes(vec2 uv)
{
  float geomDepth = getLinearDepthLowRes(uv);
  return UVAndDepthToViewPos(uv, geomDepth);
}
#endif

#ifdef SSR_CustomZMap
float getLinearDepthCustom(vec2 uv)
{
  float z = vTexture(CustomZMap, uv).x;
  return ConvertZBufferToZView(z);
}

vec3 UVToViewPosCustom(vec2 uv)
{
  float geomDepth = getLinearDepthCustom(uv);
  return UVAndDepthToViewPos(uv, geomDepth);
}
#endif
